package test.instrumentation.classfiletest;

public class AlternativeInstrumentationMethodInterface {

	interface ClassInstrumentation {
		void before();

		void after();

		/**
		 * Enables error handling to be extracted. If the exception should be propogated to the caller, the method body
		 * should throw the error. In reality, this could be added to a sub interface. This prevents the need to
		 * effectively create a try/catch around every method that you may not wish to do.
		 */
		void catchBody(Exception e) throws Exception;

		/**
		 * Re-definition of the version above using generics.
		 * 
		 * Does this even make sense??!
		 * 
		 * @param <T>
		 *            The Exception type thrown by the instrumentation method
		 * @param <U>
		 *            The corresponding thrown type. Must be a subclass of T.
		 * @param e
		 *            The exception
		 * @throws U
		 *             The Throws declaration
		 */
		<T extends Exception, U extends T> void catchBodyGenerics(T e) throws U;

		/**
		 * Alternative declaration. Has the advantage of preventing arbitrary changes to the exception thrown by the
		 * method, (although this is perfectly reasonable in a try catch block) For example: try{ // stuff throws
		 * exception }catch(ThrownException e){ //perform error handling throw new BrandNewException("blah",e); } It has
		 * the added disadvantage that the instrumentation code also needs to interrogate the return statement to decide
		 * whether the exception should be propogated.
		 * 
		 * @param e
		 *            The exception thrown by the method.
		 * @return true if the exception should be rethrown, false otherwise.
		 */
		boolean catchBodyAlternative(Exception e);

		/**
		 * Yet another alternative is to use the catchBody version declared above, but without the throws declaration.
		 * The Rethrow annotation can then be applied to the catchBody implementation to indicate that the exception
		 * should be rethrown.
		 */
		@interface Rethrow {
		}

	}

	// The annotations are still required to enable the creation of local variables or fields that are used across
	// methods.
	@interface Field {
	}

	@interface Local {
	}

	class LoggingExampleInterface implements ClassInstrumentation {

		@Override
		public void before() {
			startTime = System.currentTimeMillis();
			System.out.println("before");
		}

		@Override
		public void after() {
			final long elapsedTime = System.currentTimeMillis() - startTime;
			System.out.println("after. Elapsed: " + elapsedTime);
		}

		@Override
		public void catchBody(final Exception e) throws Exception {
			throw e;
		}

		@Local
		long startTime;

		@Override
		public boolean catchBodyAlternative(final Exception e) {
			return true;
		}

		@Override
		public <T extends Exception, U extends T> void catchBodyGenerics(
				final T e) throws U {
		}

	}
}
